package org.springframework.beans.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.util.ClassUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * AbstractBeanFactory
 *
 * @author xiaohei
 */
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {

    private List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    private ClassLoader classLoader = ClassUtil.getDefaultClassLoader();


    @Override
    public Object getBean(String name) throws BeansException {
        return doGetBean(name, null);
    }

    @Override
    public Object getBean(String name, Object... args) throws BeansException {
        return doGetBean(name, args);
    }

    private Object doGetBean(String name, Object[] args) {
        Object bean = getSingleton(name);
        if (bean != null) {
            return bean;
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name, beanDefinition, args);
    }

    @Override
    public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        beanPostProcessors.add(beanPostProcessor);
    }

    @Override
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    @Override
    public void setClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    protected BeanPostProcessorCache getBeanPostProcessorCache() {
        BeanPostProcessorCache bppCache = new BeanPostProcessorCache();
        for (BeanPostProcessor bpp : beanPostProcessors) {
            if (bpp instanceof InstantiationAwareBeanPostProcessor) {
                bppCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bpp);
            }
        }
        return bppCache;
    }

    /**
     * createBean
     *
     * @param name
     * @param beanDefinition
     * @return
     */
    protected abstract Object createBean(String name, BeanDefinition beanDefinition, Object[] args);

    /**
     * getBeanDefinition
     *
     * @param name beanDefinitionName
     * @return
     * @throws BeansException
     */
    public abstract BeanDefinition getBeanDefinition(String name) throws BeansException;

    public static class BeanPostProcessorCache {

        List<InstantiationAwareBeanPostProcessor> instantiationAware = new ArrayList<>();

    }
}
